#include "optimizer.h"

using namespace std;

int read_portfolio(const char* fname, map<string, Asset*>& portfolios)
{
  string line;
  ifstream myfile;
  vector<string> entries;
  vector<string> names;
  int ret = 0;

  myfile.open(fname);

  if (!myfile.is_open()) {
    cout<<"Open input file error!"<<endl;
    return (ret = -1);
  } // if

  // we ignore the first line
  if (getline(myfile, line).eof()) {
    // read first line error
    cout<<"Input file error!"<<endl;
    return (ret = -1);
  }

  // get the asset name
  ret = StringUtils::split(line, "\t", entries);
  if (ret == 0) {
    cout<<"Format error!"<<endl;
    return (ret = -1);
  }

  int size = entries.size();
  names.push_back(entries[size - 3]);
  names.push_back(entries[size - 2]);
  names.push_back(entries[size - 1]);

  entries.clear();

  while (!myfile.eof()) {

    getline(myfile, line);

    if (myfile.eof()) {
      cout<<"Reach the end of file!"<<endl;
      break;
    }

    if (myfile.bad()) {
      cout<<"Open input file error!"<<endl;
      ret = -1;
      break;
    }

    // parse the line
    ret = StringUtils::split(line, "\t", entries);
    if (ret == 0)
      cout<<"Format error!"<<endl;

    //for (int i = 0; i < entries.size(); ++i)
    //{
    //  cout<<i<<": "<<entries[i]<<endl;
    //}
    // Data format:
    // Asset Name | Minimum | Weight | Maximum | Mean | Std | Cash | Bond | Stock
    string name = entries[0];

    float mi = atof(entries[1].c_str());
    float weight = atof(entries[2].c_str());
    float ma = atof(entries[3].c_str());
    float mean = atof(entries[4].c_str());
    float std = atof(entries[5].c_str());

    map<string, float> coef;

    coef.insert(pair<string, float>(names[0], atof(entries[6].c_str())));
    coef.insert(pair<string, float>(names[1], atof(entries[7].c_str())));
    coef.insert(pair<string, float>(names[2], atof(entries[8].c_str())));

    entries.clear();

    Asset* asset = new Asset(name, weight, mi, ma);

    asset->setup();
    asset->sampling(mean, std);
    asset->coefficient(coef); // for demonstrating, we directly get the coefficient here.

    portfolios.insert(pair<string, Asset*>(name, asset));
  } //  while

  if (portfolios.size() > 0) {
    map<string, Asset*>::iterator iter1;
    map<string, Asset*>::iterator iter2;
    for (iter1 = portfolios.begin(); iter1 != portfolios.end(); ++iter1)
    {
      Asset* outer = iter1->second;

      for (iter2 = portfolios.begin(); iter2 != portfolios.end(); ++iter2)
      {
        Asset* inner = iter2->second;

        outer->assets.insert(pair<string, Asset*>(inner->name, inner));
      }

      outer->covariance();
    }
  }

  return ret;
}

int utility(const vector<float>& weights, const vector<float>& means, const vector<float>& covariances, vector<float>& utilities, float risk, float threshold)
{
  int size = weights.size();
  int ret = 0;

  for (int i = 0; i < size; ++i)
  {
    float co = 0;
    for (int j = 0; j < size; ++j)
    {
      co += weights[j] * covariances[i * size + j];
    }
    // Sharpe's algorithm Utility = Expect - ((Std^2)/risk)
    utilities[i] = means[i] - (float)2 * co / risk;
  }

  return ret;
}

int find_factors(const vector<float>& weights, const vector<float>& lbounds, const vector<float>& ubounds,
                    const vector<float>& utilities, vector<float>& factors)
{
  int size = weights.size();
  int iadd = 0, isub = 0;
  bool switched1 = false, switched2 = false;
  int ret = 0;

  // find the best utility to add (compare each utility within weight limitation)
  float largest = -1;
  float lowest = FLT_MAX;
  for (int i = 0; i < size; ++i)
  {
    if (weights[i] < ubounds[i]) {
      iadd    = (utilities[i] > largest) ? i : iadd;
      largest = utilities[iadd];
      switched1 = true;
    }

    if (weights[i] > lbounds[i]) {
      isub    = (utilities[i] < lowest) ? i : isub;
      lowest  = utilities[isub];
      switched2 = true;
    }

    cout<<"Utility: "<<utilities[i]<<", largest: "<<largest<<", lowest: "<<lowest<<endl;
  }

  factors[0] = iadd;
  factors[1] = isub;

  return (!switched1 || !switched2) ? (ret = 0) : (ret = 1);
}


int optimizer(map<string, Asset*> portfolios, float risk, float threshold)
{
  int size;
  int ret = 0;

  size = portfolios.size();

  // Load the data from assets
  vector<string> names(size);
  vector<float> weights(size);
  vector<float> utilities(size);
  vector<float> means(size);
  vector<float> covariances;
  vector<float> lbounds(size);
  vector<float> ubounds(size);

  cout<<endl<<"Covariance Matrix: "<<endl;
  map<string, Asset*>::iterator iter;
  int i = 0, j = 0;
  for (iter = portfolios.begin(); iter != portfolios.end(); ++iter)
  {
    Asset* asset = iter->second;

    names[i]    = asset->name;
    weights[i]  = asset->weight;
    means[i]    = asset->mean;
    lbounds[i]   = asset->min;
    ubounds[i]   = asset->max;

    map<string, float>::iterator iter1;
    for (iter1 = asset->covariances.begin(); iter1 != asset->covariances.end(); ++iter1)
    {
      float cv = iter1->second;

      covariances.push_back(cv);

      cout<<cv<<"\t";
      j++;
    }
    cout<<endl;

    i++;
  }

  bool done = false;
  while (!done) {
    // Compute marginal utilities for each asset
    ret = utility(weights, means, covariances, utilities, risk, threshold);

    cout<<"Start weight: "<<names[0]<<"("<<weights[0]<<"), "<<names[1]<<"("<<weights[1]<<"), "<<names[2]<<"("<<weights[2]<<")"<<endl;
    cout<<"Start utility: "<<names[0]<<"("<<utilities[0]<<"), "<<names[1]<<"("<<utilities[1]<<"), "<<names[2]<<"("<<utilities[2]<<")"<<endl;

    // find best factors to add and subtract
    vector<float> factors(2);
    ret = find_factors(weights, lbounds, ubounds, utilities, factors);
    cout<<"Add factor: "<<names[factors[0]]<<" with utility: "<<utilities[factors[0]]<<endl;
    cout<<"Sub factor: "<<names[factors[1]]<<" with utility: "<<utilities[factors[1]]<<endl;

    float iadd = factors[0];
    float isub = factors[1];
    // Check completion or not
    if (ret == 0) {
      done = true;
    } else {
      if ((utilities[iadd] - utilities[isub]) < threshold) {
        done = true;
      }
    }

    float var;
    float delta;
    if (!done) {
      // Calculate the step size, get the variance of the different of largest and lowest assets
      var = covariances[iadd * size + iadd] - 2 * covariances[iadd * size + isub] + covariances[isub * size + isub];
      delta = (utilities[iadd] - utilities[isub]) / (2 * var / risk);

      cout<<"Delta: "<<delta<<endl;

      delta = ((weights[iadd] + delta) > ubounds[iadd]) ? (ubounds[iadd] - weights[iadd]) : delta;
      delta = ((weights[isub] - delta) < lbounds[isub]) ? (weights[isub] - lbounds[isub]) : delta;

      cout<<"Delta: "<<delta<<endl;

      weights[iadd] += delta;
      weights[isub] -= delta;
    }
  } // end while

  for (i = 0; i < size; ++i)
  {
    cout<<endl<<names[i]<<": "<<endl;
    cout<<"\t Weight: "<<weights[i]<<", Utility: "<<utilities[i]<<endl;
  }


  return ret;
}

int main()
{
  map<string, Asset*> portfolios;
  map<string, Asset*>::iterator iter;
  pair<map<string, Asset*>::iterator, bool> port2;
  float risk = 50;
  float threshold = 0.0001;
  int ret = 0;

  ret = read_portfolio("input", portfolios);
  if (ret == -1) {
    cout<<"Execute read_portfolio() error!"<<endl;
  }

  for (iter = portfolios.begin(); iter != portfolios.end(); ++iter)
  {
    Asset * asset = iter->second;

     asset->print();
  }

  // Optimizer loop
  optimizer(portfolios, risk, threshold);

  return 0;
}
